home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / games / dronezone / dzdrone.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  52.6 KB  |  1,899 lines

  1. /*
  2.  *    File:        DZDrone.c
  3.  *
  4.  *    Contents:    A drone object represents the position of an player or drone.
  5.  *
  6.  *    Copyright © 1996 Apple Computer, Inc.
  7.  */
  8.  
  9. #include <assert.h>
  10. #include <math.h>
  11. #include <stdlib.h>
  12.  
  13. #include <Components.h>
  14. #include <Dialogs.h>
  15. #include <Events.h>
  16. #include <QuickDraw.h>
  17. #include <Resources.h>
  18. #include <Sound.h>
  19. #include <SoundComponents.h>
  20. #include <SoundInput.h>
  21. #include <ToolUtils.h>
  22. #include <Types.h>
  23.  
  24. #include <QD3D.h>
  25. #include <QD3DCamera.h>
  26. #include <QD3DDrawContext.h>
  27. #include <QD3DGeometry.h>
  28. #include <QD3DMath.h>
  29. #include <QD3DPick.h>
  30. #include <QD3DSet.h>
  31. #include <QD3DShader.h>
  32. #include <QD3DStyle.h>
  33. #include <QD3DTransform.h>
  34. #include <QD3DView.h>
  35.  
  36. #include "SoundSprocket.h"
  37.  
  38. #include "DZDisplay.h"
  39. #include "DZDrone.h"
  40. #include "DZGame.h"
  41. #include "DZResource.h"
  42. #include "DZSound.h"
  43. #include "DZThumbprint.h"
  44. #include "DZUtils.h"
  45.  
  46. #ifndef    SELF_DRONE_HAS_FRICTION
  47. #define    SELF_DRONE_HAS_FRICTION    1
  48. #endif
  49.  
  50.  
  51. #define IS_SELF_DRONE(inDrone)        (inDrone->thumbprint == kThumbprint_SelfDrone)
  52. #define IS_AUTO_DRONE(inDrone)        (inDrone->thumbprint == kThumbprint_AutoDrone)
  53. #define IS_BULLET_DRONE(inDrone)    (inDrone->thumbprint == kThumbprint_BulletDrone)
  54. #define IS_DRONE(inDrone)            (IS_SELF_DRONE(inDrone) || IS_AUTO_DRONE(inDrone) || IS_BULLET_DRONE(inDrone))
  55.  
  56. #define SELF_DRONE_INITIAL_SPEED    0.50    // In units per second
  57. #define SELF_DRONE_MAX_ACCEL        10.0        // In units per second per second
  58. #define SELF_DRONE_FRICTION            0.80     // as a percentage (1.00 would be no friction)
  59.  
  60. #define AUTO_DRONE_LEAD                5.0        // Drone target leads by this distance (in units)
  61. #define AUTO_DRONE_ACCEL            2.0        // Drone acceleration (units per second per second)
  62. #define AUTO_DRONE_TURN_RATE        1.8        // In radians per second
  63. #define AUTO_DRONE_BURN_TIME        3.0        // In seconds
  64. #define AUTO_DRONE_REST_TIME        1.0        // In seconds
  65. #define AUTO_DRONE_EXPLOSION_TIME    3.0        // In seconds
  66. #define AUTO_DRONE_FADE_TIME        0.25    // Fraction of AUTO_DRONE_EXPLOSION_TIME for fade
  67. #define AUTO_DRONE_MIN_SCALE        0.2        // Initial scale for explosion
  68.  
  69. #define AUTO_DRONE_IDLE_REF_DIST    2.0
  70. #define AUTO_DRONE_BURN_REF_DIST    5.0
  71. #define AUTO_DRONE_EXPL_REF_DIST    30.0
  72.  
  73. #define BULLET_DRONE_OFFSET            0.2        // Offset from self to bullet initial position
  74. #define BULLET_DRONE_SPEED            50.0    // In units per second
  75. #define BULLET_DRONE_LIMIT            50.0    // Maximum length of a bullet
  76.  
  77. #define HUD_SCALE                    0.02    // Scale of HUD
  78. #define HUD_HEIGHT                    0.5        // cos(angle) at which to consider out-of-plane
  79.  
  80.  
  81. enum {
  82.     kDroneDrawContextSize            = 5        // Used for picking
  83. };
  84.  
  85.  
  86. typedef enum TDroneOrder {                    // In order of evaluation
  87.     kDroneOrder_Self,
  88.     kDroneOrder_Auto,
  89.     kDroneOrder_Bullet
  90. } TDroneOrder;
  91.  
  92.  
  93. typedef void (*TDroneMoveMethod)(
  94.     TDroneObject            inDrone);
  95.  
  96. typedef void (*TDroneUpdateSoundMethod)(
  97.     TDroneObject            inDrone);
  98.  
  99. typedef void (*TDroneSubmitMethod)(
  100.     TDroneObject            inDrone,
  101.     Boolean                    inHUDVisible,
  102.     TQ3ViewObject            inView);
  103.  
  104. typedef void (*TDronePickSubmitMethod)(
  105.     TDroneObject            inDrone,
  106.     TQ3ViewObject            inView);
  107.  
  108. typedef void (*TDroneHitMethod)(
  109.     TDroneObject            inDrone);
  110.  
  111.  
  112. typedef enum TAutoSound {
  113.     kAutoSound_None,
  114.     kAutoSound_Idle,
  115.     kAutoSound_Burn,
  116.     kAutoSound_Explosion
  117. } TAutoSound;
  118.  
  119. typedef enum TAutoMode {
  120.     kAutoMode_Idle,
  121.     kAutoMode_Burn,
  122.     kAutoMode_Rest,
  123.     kAutoMode_Explosion
  124. } TAutoMode;
  125.  
  126.  
  127. typedef struct TDroneData {
  128.     TThumbprint                thumbprint;            // For validation
  129.     
  130.     TDroneMoveMethod        moveMethod;            // Method: Drone_Move
  131.     TDroneUpdateSoundMethod    updateSoundMethod;    // Method: Drone_UpdateSound
  132.     TDroneSubmitMethod        submitMethod;        // Method: Drone_Submit
  133.     TDronePickSubmitMethod    pickSubmitMethod;    // Method: Drone_PickSubmit
  134.     TDroneHitMethod            hitMethod;            // Method: Drone_Hit
  135.     
  136.     TDroneObject            prev;                // The global list of drones
  137.     TDroneObject            next;
  138.     TDroneOrder                order;                // Sort key
  139.     
  140.     Boolean                    mark;                // Is this drone marked to die?
  141.     
  142.     TQ3Point3D                position;            // Current position
  143.     TQ3Point3D                position1;            // Previous position
  144.     
  145.     TQ3Vector3D                velocity;            // Change in position over time
  146.     TQ3Vector3D                velocity1;            // Previous velocity
  147.     
  148.     TQ3Vector3D                acceleration;        // Change in velocity over time
  149.     
  150.     TQ3Vector3D                direction;            // Forward        (model X axis)
  151.     TQ3Vector3D                up;                    // Vertical        (model Y axis)
  152.     TQ3Vector3D                cross;                // Horizontal    (model Z axis)
  153.     
  154.     TQ3Object                geometry;            // The shape of the drone
  155.     
  156.     SndChannelPtr            autoSndChannel;        // Auto: Sound channel
  157.     SSpSourceReference        autoSource;            // Auto: 3D sound source
  158.     TAutoSound                autoSound;            // Auto: Sound currently playing
  159.     TDroneObject            autoInterest;        // Auto: Drone that we're looking at
  160.     TQ3Vector3D                autoVelocity;        // Auto: Drone velocity (instantaneous)
  161.     TAutoMode                autoMode;            // Auto: Current state
  162.     unsigned long            autoModeTimeout;    // Auto: When does mode expire?
  163.     float                    autoDistance;        // Auto: Last distance from target
  164.     float                    autoExplosion;        // Auto: 0=start; 1=end of explosion
  165.     
  166.     TQ3Point3D                bulletOrigin;        // Bullet: Starting position of the bullet
  167. } TDroneData;
  168.  
  169.  
  170. static TDroneObject            gDroneList                        = NULL;
  171. static TQ3Object            gDroneAutoGeometry                = NULL;
  172. static TQ3Object            gDroneAutoBurnGeometry            = NULL;
  173. static TQ3Object            gDroneAutoExplosionGeometry        = NULL;
  174. static TQ3AttributeSet        gDroneBulletColor                = NULL;
  175. static TQ3ShaderObject        gDroneNULLIllumination            = NULL;
  176. static TQ3ViewObject        gDroneView                        = NULL;
  177. static TQ3DrawContextObject    gDroneDrawContext                = NULL;
  178. static TQ3CameraObject        gDroneCamera                    = NULL;
  179. static TQ3PickObject        gDronePick                        = NULL;
  180.  
  181. static SndListHandle        gDroneAutoSndIdle                = NULL;
  182. static SndListHandle        gDroneAutoSndBurn                = NULL;
  183. static SndListHandle        gDroneAutoSndExplosion            = NULL;
  184.  
  185. static long                    gDroneAutoSndIdleOffset            = 0;
  186. static long                    gDroneAutoSndBurnOffset            = 0;
  187. static long                    gDroneAutoSndExplosionOffset    = 0;
  188.  
  189. static TQ3GeometryObject    gDroneAutoMarkerEqual            = NULL;
  190. static TQ3GeometryObject    gDroneAutoMarkerAbove            = NULL;
  191. static TQ3GeometryObject    gDroneAutoMarkerBelow            = NULL;
  192.  
  193. static unsigned char        gDroneAutoMarkerDataEqual[8] =
  194.                                     {0x38, 0x44, 0x82, 0x92, 0x82, 0x44, 0x38, 0x00};
  195.  
  196. static unsigned char        gDroneAutoMarkerDataAbove[8] =
  197.                                     {0x38, 0x54, 0x92, 0xFE, 0x92, 0x54, 0x38, 0x00};
  198.  
  199. static unsigned char        gDroneAutoMarkerDataBelow[8] =
  200.                                     {0x38, 0x44, 0x82, 0xFE, 0x82, 0x44, 0x38, 0x00};
  201.  
  202. static Boolean                gDroneAutoCheckFilterVersion    = true;
  203.  
  204.  
  205. static TDroneObject Drone_New(
  206.     TDroneOrder                inOrder);
  207.  
  208. static void SelfDrone_Move(
  209.     TDroneObject            inDrone);
  210.  
  211. static void AutoDrone_Move(
  212.     TDroneObject            inDrone);
  213.  
  214. static void BulletDrone_Move(
  215.     TDroneObject            inDrone);
  216.  
  217. static void AutoDrone_UpdateSound(
  218.     TDroneObject            inDrone);
  219.  
  220. static void AutoDrone_Submit(
  221.     TDroneObject            inDrone,
  222.     Boolean                    inHUDVisible,
  223.     TQ3ViewObject            inView);
  224.  
  225. static void BulletDrone_Submit(
  226.     TDroneObject            inDrone,
  227.     Boolean                    inHUDVisible,
  228.     TQ3ViewObject            inView);
  229.  
  230. static void Drone_PickSubmit(
  231.     TDroneObject            inDrone,
  232.     TQ3ViewObject            inView);
  233.  
  234. static void AutoDrone_PickSubmit(
  235.     TDroneObject            inDrone,
  236.     TQ3ViewObject            inView);
  237.  
  238. static void Drone_Hit(
  239.     TDroneObject            inDrone);
  240.  
  241. static void AutoDrone_Hit(
  242.     TDroneObject            inDrone);
  243.  
  244. void Drone_GetMatrix(
  245.     TDroneObject            inDrone,
  246.     TQ3Matrix4x4*            outMatrix);
  247.  
  248.  
  249. /* =============================================================================
  250.  *        Drone_Init (external)
  251.  *
  252.  *    Initializes the drone stuff.
  253.  * ========================================================================== */
  254. void Drone_Init(
  255.     void)
  256. {
  257.     TQ3ColorRGB                        color;
  258.     TQ3PixmapDrawContextData        pixmapDrawContextData;
  259.     TQ3ViewAngleAspectCameraData    viewAngleCameraData;
  260.     TQ3WindowPointPickData            windowPointPickData;
  261.     TQ3MarkerData                    markerData;
  262.     
  263.     // Set up the autopilot drone geometry
  264.     gDroneAutoGeometry = Get3DMFResource(k3DMFID_AutoDrone);
  265.     assert(gDroneAutoGeometry != NULL);
  266.     
  267.     // Set up the autopilot drone burn geometry
  268.     gDroneAutoBurnGeometry = Get3DMFResource(k3DMFID_AutoDroneBurn);
  269.     assert(gDroneAutoBurnGeometry != NULL);
  270.     
  271.     // Set up the autopilot drone explosion geometry
  272.     gDroneAutoExplosionGeometry = Get3DMFResource(k3DMFID_AutoDroneExplosion);
  273.     assert(gDroneAutoExplosionGeometry != NULL);
  274.     
  275.     // Read in the autopilot drone sounds
  276.     gDroneAutoSndIdle = (SndListHandle) GetResource('snd ', kSndID_AutoIdle);
  277.     assert(gDroneAutoSndIdle != NULL);
  278.     
  279.     gDroneAutoSndBurn = (SndListHandle) GetResource('snd ', kSndID_AutoBurn);
  280.     assert(gDroneAutoSndBurn != NULL);
  281.     
  282.     gDroneAutoSndExplosion = (SndListHandle) GetResource('snd ', kSndID_AutoExplosion);
  283.     assert(gDroneAutoSndExplosion != NULL);
  284.     
  285.     GetSoundHeaderOffset(gDroneAutoSndIdle,            &gDroneAutoSndIdleOffset);
  286.     GetSoundHeaderOffset(gDroneAutoSndBurn,            &gDroneAutoSndBurnOffset);
  287.     GetSoundHeaderOffset(gDroneAutoSndExplosion,    &gDroneAutoSndExplosionOffset);
  288.     
  289.     // Set up the bullet drone line color
  290.     gDroneBulletColor = Q3AttributeSet_New();
  291.     assert(gDroneBulletColor != NULL);
  292.     
  293.     color.r = 1.0;
  294.     color.g = 0.8;
  295.     color.b = 0.1;
  296.     
  297.     Q3AttributeSet_Add(gDroneBulletColor, kQ3AttributeTypeDiffuseColor, &color);
  298.     
  299.     // Create the bullet null illum shader
  300.     gDroneNULLIllumination = Q3NULLIllumination_New();
  301.     assert(gDroneNULLIllumination != NULL);
  302.     
  303.     // Create the view that we use for bullet collision detection
  304.     gDroneView = Q3View_New();
  305.     assert(gDroneView != NULL);
  306.     
  307.     // Create its draw context
  308.     pixmapDrawContextData.drawContextData.clearImageMethod        = kQ3ClearMethodWithColor;
  309.     pixmapDrawContextData.drawContextData.clearImageColor.a        = 1.0;
  310.     pixmapDrawContextData.drawContextData.clearImageColor.r        = 0.0;
  311.     pixmapDrawContextData.drawContextData.clearImageColor.g        = 0.0;
  312.     pixmapDrawContextData.drawContextData.clearImageColor.b        = 0.0;
  313.     pixmapDrawContextData.drawContextData.paneState                = kQ3False;
  314.     pixmapDrawContextData.drawContextData.maskState                = kQ3False;
  315.     pixmapDrawContextData.drawContextData.doubleBufferState        = kQ3False;
  316.     pixmapDrawContextData.pixmap.width                            = kDroneDrawContextSize;
  317.     pixmapDrawContextData.pixmap.height                            = kDroneDrawContextSize;
  318.     pixmapDrawContextData.pixmap.rowBytes                        = pixmapDrawContextData.pixmap.width*4;
  319.     pixmapDrawContextData.pixmap.pixelSize                        = 32;
  320.     pixmapDrawContextData.pixmap.pixelType                        = kQ3PixelTypeRGB32;
  321.     pixmapDrawContextData.pixmap.bitOrder                        = kQ3EndianBig;
  322.     pixmapDrawContextData.pixmap.byteOrder                        = kQ3EndianBig;
  323.     pixmapDrawContextData.pixmap.image                            = malloc(pixmapDrawContextData.pixmap.height*pixmapDrawContextData.pixmap.rowBytes);
  324.     
  325.     gDroneDrawContext = Q3PixmapDrawContext_New(&pixmapDrawContextData);
  326.     assert(gDroneDrawContext != NULL);
  327.     
  328.     Q3View_SetDrawContext(gDroneView, gDroneDrawContext);
  329.     
  330.     // Create its camera
  331.     viewAngleCameraData.cameraData.placement.cameraLocation.x    = 0.0;
  332.     viewAngleCameraData.cameraData.placement.cameraLocation.y    = 0.0;
  333.     viewAngleCameraData.cameraData.placement.cameraLocation.z    = 0.0;
  334.     viewAngleCameraData.cameraData.placement.pointOfInterest.x    = 1.0;
  335.     viewAngleCameraData.cameraData.placement.pointOfInterest.y    = 0.0;
  336.     viewAngleCameraData.cameraData.placement.pointOfInterest.z    = 0.0;
  337.     viewAngleCameraData.cameraData.placement.upVector.x            = 0.0;
  338.     viewAngleCameraData.cameraData.placement.upVector.y            = 1.0;
  339.     viewAngleCameraData.cameraData.placement.upVector.z            = 0.0;
  340.     viewAngleCameraData.cameraData.range.hither                    = 0.1;
  341.     viewAngleCameraData.cameraData.range.yon                    = BULLET_DRONE_LIMIT;
  342.     viewAngleCameraData.cameraData.viewPort.origin.x            = -1.0;
  343.     viewAngleCameraData.cameraData.viewPort.origin.y            = 1.0;
  344.     viewAngleCameraData.cameraData.viewPort.width                = 2.0;
  345.     viewAngleCameraData.cameraData.viewPort.height                = 2.0;
  346.     viewAngleCameraData.fov                                        = 0.1;
  347.     viewAngleCameraData.aspectRatioXToY                            = pixmapDrawContextData.pixmap.width/pixmapDrawContextData.pixmap.height;
  348.  
  349.     gDroneCamera = Q3ViewAngleAspectCamera_New(&viewAngleCameraData);
  350.     assert(gDroneCamera != NULL);
  351.     
  352.     Q3View_SetCamera(gDroneView, gDroneCamera);
  353.     
  354.     // Create the pick object
  355.     windowPointPickData.data.sort                = kQ3PickSortNearToFar;
  356.     windowPointPickData.data.mask                = kQ3PickDetailMaskPickID | kQ3PickDetailMaskDistance;
  357.     windowPointPickData.data.numHitsToReturn    = kQ3ReturnAllHits;
  358.     windowPointPickData.point.x                    = 0.5*pixmapDrawContextData.pixmap.width;
  359.     windowPointPickData.point.y                    = 0.5*pixmapDrawContextData.pixmap.height;
  360.     windowPointPickData.vertexTolerance            = 0.0;
  361.     windowPointPickData.edgeTolerance            = 0.0;
  362.     
  363.     gDronePick = Q3WindowPointPick_New(&windowPointPickData);
  364.     assert(gDronePick != NULL);
  365.     
  366.     // Create the autodrone markers
  367.     markerData.location.x            = 0.0;
  368.     markerData.location.y            = 0.0;
  369.     markerData.location.z            = 0.0;
  370.     markerData.xOffset                = -3;
  371.     markerData.yOffset                = -3;
  372.     markerData.bitmap.width            = 8;
  373.     markerData.bitmap.height        = 8;
  374.     markerData.bitmap.rowBytes        = 1;
  375.     markerData.bitmap.bitOrder        = kQ3EndianBig;
  376.     markerData.markerAttributeSet    = Q3AttributeSet_New();
  377.     
  378.     assert(markerData.markerAttributeSet != NULL);
  379.     
  380.     color.r = 1.0;
  381.     color.g = 1.0;
  382.     color.b = 0.4;
  383.     
  384.     Q3AttributeSet_Add(markerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color);
  385.     
  386.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataEqual;
  387.     gDroneAutoMarkerEqual = Q3Marker_New(&markerData);
  388.     assert(gDroneAutoMarkerEqual != NULL);
  389.     
  390.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataAbove;
  391.     gDroneAutoMarkerAbove = Q3Marker_New(&markerData);
  392.     assert(gDroneAutoMarkerAbove != NULL);
  393.     
  394.     markerData.bitmap.image = (unsigned char*) gDroneAutoMarkerDataBelow;
  395.     gDroneAutoMarkerBelow = Q3Marker_New(&markerData);
  396.     assert(gDroneAutoMarkerBelow != NULL);
  397.     
  398.     Q3Object_Dispose(markerData.markerAttributeSet);
  399.     markerData.markerAttributeSet = NULL;
  400. }
  401.  
  402.  
  403. /* =============================================================================
  404.  *        Drone_Exit (external)
  405.  *
  406.  *    Prepares for exit.
  407.  * ========================================================================== */
  408. void Drone_Exit(
  409.     void)
  410. {
  411.     while (gDroneList != NULL)
  412.     {
  413.         Drone_Dispose(gDroneList);
  414.     }
  415.     
  416.     if (gDroneAutoGeometry != NULL)
  417.     {
  418.         Q3Object_Dispose(gDroneAutoGeometry);
  419.         gDroneAutoGeometry = NULL;
  420.     }
  421.     
  422.     if (gDroneAutoBurnGeometry != NULL)
  423.     {
  424.         Q3Object_Dispose(gDroneAutoBurnGeometry);
  425.         gDroneAutoBurnGeometry = NULL;
  426.     }
  427.     
  428.     if (gDroneAutoExplosionGeometry != NULL)
  429.     {
  430.         Q3Object_Dispose(gDroneAutoExplosionGeometry);
  431.         gDroneAutoExplosionGeometry = NULL;
  432.     }
  433.     
  434.     if (gDroneAutoSndIdle != NULL)
  435.     {
  436.         ReleaseResource((Handle) gDroneAutoSndIdle);
  437.         gDroneAutoSndIdle = NULL;
  438.     }
  439.     
  440.     if (gDroneAutoSndBurn != NULL)
  441.     {
  442.         ReleaseResource((Handle) gDroneAutoSndBurn);
  443.         gDroneAutoSndBurn = NULL;
  444.     }
  445.     
  446.     if (gDroneAutoSndExplosion != NULL)
  447.     {
  448.         ReleaseResource((Handle) gDroneAutoSndExplosion);
  449.         gDroneAutoSndExplosion = NULL;
  450.     }
  451.     
  452.     if (gDroneBulletColor != NULL)
  453.     {
  454.         Q3Object_Dispose(gDroneBulletColor);
  455.         gDroneBulletColor = NULL;
  456.     }
  457.     
  458.     if (gDroneNULLIllumination != NULL)
  459.     {
  460.         Q3Object_Dispose(gDroneNULLIllumination);
  461.         gDroneNULLIllumination = NULL;
  462.     }
  463.     
  464.     if (gDroneView != NULL)
  465.     {
  466.         Q3Object_Dispose(gDroneView);
  467.         gDroneView = NULL;
  468.     }
  469.     
  470.     if (gDroneDrawContext != NULL)
  471.     {
  472.         Q3Object_Dispose(gDroneDrawContext);
  473.         gDroneDrawContext = NULL;
  474.     }
  475.     
  476.     if (gDroneCamera != NULL)
  477.     {
  478.         Q3Object_Dispose(gDroneCamera);
  479.         gDroneCamera = NULL;
  480.     }
  481.     
  482.     if (gDronePick != NULL)
  483.     {
  484.         Q3Object_Dispose(gDronePick);
  485.         gDronePick = NULL;
  486.     }
  487.     
  488.     if (gDroneAutoMarkerEqual != NULL)
  489.     {
  490.         Q3Object_Dispose(gDroneAutoMarkerEqual);
  491.         gDroneAutoMarkerEqual = NULL;
  492.     }
  493.     
  494.     if (gDroneAutoMarkerAbove != NULL)
  495.     {
  496.         Q3Object_Dispose(gDroneAutoMarkerAbove);
  497.         gDroneAutoMarkerAbove = NULL;
  498.     }
  499.     
  500.     if (gDroneAutoMarkerBelow != NULL)
  501.     {
  502.         Q3Object_Dispose(gDroneAutoMarkerBelow);
  503.         gDroneAutoMarkerBelow = NULL;
  504.     }
  505. }
  506.  
  507.  
  508. /* =============================================================================
  509.  *        Drone_New (internal)
  510.  *
  511.  *    Creates a new drone.
  512.  * ========================================================================== */
  513. TDroneObject Drone_New(
  514.     TDroneOrder            inOrder)
  515. {
  516.     TDroneObject        drone;
  517.     TDroneObject        prev;
  518.     TDroneObject        next;
  519.     
  520.     // Allocate the memory
  521.     drone = (TDroneObject) malloc(sizeof(TDroneData));
  522.     assert(drone != NULL);
  523.     
  524.     drone->thumbprint = kThumbprint_Dead;  // until finished with it
  525.     
  526.     // Find where to insert it
  527.     prev = NULL;
  528.     next = gDroneList;
  529.     while (next != NULL && next->order < inOrder)
  530.     {
  531.         prev = next;
  532.         next = next->next;
  533.     }
  534.     
  535.     // Link it into the list
  536.     drone->prev = prev;
  537.     drone->next = next;
  538.     
  539.     if (prev != NULL)
  540.     {
  541.         prev->next = drone;
  542.     }
  543.     else
  544.     {
  545.         gDroneList = drone;
  546.     }
  547.     
  548.     if (next != NULL)
  549.     {
  550.         next->prev = drone;
  551.     }
  552.     
  553.     // Fill in the defaults
  554.     drone->moveMethod            = NULL;
  555.     drone->updateSoundMethod    = NULL;
  556.     drone->submitMethod            = NULL;
  557.     drone->pickSubmitMethod        = NULL;
  558.     drone->hitMethod            = NULL;
  559.     
  560.     drone->order                = inOrder;
  561.     
  562.     drone->mark                    = false;
  563.     
  564.     drone->position.x            = 0.0;
  565.     drone->position.y            = 0.0;
  566.     drone->position.z            = 0.0;
  567.     
  568.     drone->position1.x            = 0.0;
  569.     drone->position1.y            = 0.0;
  570.     drone->position1.z            = 0.0;
  571.     
  572.     drone->velocity.x            = 0.0;
  573.     drone->velocity.y            = 0.0;
  574.     drone->velocity.z            = 0.0;
  575.     
  576.     drone->velocity1.x            = 0.0;
  577.     drone->velocity1.y            = 0.0;
  578.     drone->velocity1.z            = 0.0;
  579.     
  580.     drone->acceleration.x        = 0.0;
  581.     drone->acceleration.y        = 0.0;
  582.     drone->acceleration.z        = 0.0;
  583.     
  584.     drone->direction.x            = 1.0;
  585.     drone->direction.y            = 0.0;
  586.     drone->direction.z            = 0.0;
  587.     
  588.     drone->up.x                    = 0.0;
  589.     drone->up.y                    = 1.0;
  590.     drone->up.z                    = 0.0;
  591.     
  592.     drone->cross.x                = 0.0;
  593.     drone->cross.y                = 0.0;
  594.     drone->cross.z                = 1.0;
  595.     
  596.     drone->geometry                = NULL;
  597.     
  598.     drone->autoSndChannel        = NULL;
  599.     drone->autoSource            = NULL;
  600.     drone->autoSound            = kAutoSound_None;
  601.     
  602.     drone->autoInterest            = NULL;
  603.     
  604.     drone->autoVelocity.x        = 0.0;
  605.     drone->autoVelocity.y        = 0.0;
  606.     drone->autoVelocity.z        = 0.0;
  607.     
  608.     drone->autoMode                = kAutoMode_Idle;
  609.     drone->autoModeTimeout        = 0;
  610.     drone->autoDistance            = 0.0;
  611.     drone->autoExplosion        = 0.0;
  612.     
  613.     drone->bulletOrigin.x        = 0.0;
  614.     drone->bulletOrigin.y        = 0.0;
  615.     drone->bulletOrigin.z        = 0.0;
  616.     
  617.     return drone;
  618. }
  619.  
  620.  
  621. /* =============================================================================
  622.  *        SelfDrone_New (external)
  623.  *
  624.  *    Creates a new drone whose movement pattern is defined by user controls.
  625.  * ========================================================================== */
  626. TDroneObject SelfDrone_New(
  627.     void)
  628. {
  629.     TDroneObject        drone;
  630.     
  631.     // Create the basic drone
  632.     drone = Drone_New(kDroneOrder_Self);
  633.     assert(drone != NULL);
  634.     
  635.     // Fill in the fields
  636.     drone->moveMethod        = SelfDrone_Move;
  637.     
  638.     Q3Vector3D_Scale(&drone->direction, SELF_DRONE_INITIAL_SPEED, &drone->autoVelocity);
  639.     
  640.     // Validate it
  641.     drone->thumbprint        = kThumbprint_SelfDrone;
  642.     
  643.     return drone;
  644. }
  645.  
  646.  
  647. // **************************** GetSSpFilterVersion ****************************
  648. // Finds the manufacturer and version number of the SoundSprocket filter that
  649. // may be installed.  inManufacturer should be the manufacturer code specified
  650. // at the installation time, which may be zero to allow any manufacturer.
  651. // If no error is encountered, outManufacturer is set to the actual manufacturer
  652. // code and outMajorVersion and outMinorVersion are set to the component
  653. // specification level and manufacturer's implementation revision, respectively.
  654. static OSStatus GetSSpFilterVersion(
  655.     OSType                    inManufacturer,
  656.     OSType*                    outManufacturer,
  657.     UInt32*                    outMajorVersion,
  658.     UInt32*                    outMinorVersion)
  659. {
  660.     OSStatus                err;
  661.     ComponentDescription    description;
  662.     Component                componentRef;
  663.     UInt32                    vers;
  664.     
  665.     // Set up the component description
  666.     description.componentType            = kSoundEffectsType;
  667.     description.componentSubType        = kSSpLocalizationSubType;
  668.     description.componentManufacturer    = inManufacturer;
  669.     description.componentFlags            = 0;        
  670.     description.componentFlagsMask        = 0;    
  671.     
  672.     // Find a component matching the description
  673.     componentRef = FindNextComponent(nil, &description);
  674.     if (componentRef == nil)  return couldntGetRequiredComponent;
  675.     
  676.     // Get the component description (for the manufacturer code)
  677.     err = GetComponentInfo(componentRef, &description, nil, nil, nil);
  678.     if (err != noErr)  return err;
  679.     
  680.     // Get the version composite
  681.     vers = (UInt32) GetComponentVersion((ComponentInstance) componentRef);
  682.     
  683.     // Return the results
  684.     *outManufacturer = description.componentManufacturer;
  685.     *outMajorVersion = HiWord(vers);
  686.     *outMinorVersion = LoWord(vers);
  687.     
  688.     return noErr;
  689. }
  690.  
  691.  
  692. /* =============================================================================
  693.  *        AutoDrone_New (external)
  694.  *
  695.  *    Creates a new drone whose movement pattern is under automatic control.
  696.  * ========================================================================== */
  697. TDroneObject AutoDrone_New(
  698.     TDroneObject            inDroneOfInterest)
  699. {
  700.     OSStatus            err;
  701.     TDroneObject        drone;
  702.     TQ3Vector3D            orientation;
  703.     SoundComponentLink    link;
  704.     OSType                manufacturer;
  705.     UInt32                majorVersion;
  706.     UInt32                minorVersion;
  707.     
  708.     assert(inDroneOfInterest != NULL && IS_DRONE(inDroneOfInterest));
  709.     
  710.     // Create the basic drone
  711.     drone = Drone_New(kDroneOrder_Auto);
  712.     assert(drone != NULL);
  713.     
  714.     // Fill in the fields
  715.     drone->moveMethod        = AutoDrone_Move;
  716.     drone->updateSoundMethod = AutoDrone_UpdateSound;
  717.     drone->submitMethod        = AutoDrone_Submit;
  718.     drone->pickSubmitMethod    = AutoDrone_PickSubmit;
  719.     drone->hitMethod        = AutoDrone_Hit;
  720.     
  721.     // Allocate the sound channel and set up for 3D localized sound
  722.     drone->autoSndChannel    = NULL;
  723.     SndNewChannel(&drone->autoSndChannel, sampledSynth, initMono, NULL);
  724.     assert(drone->autoSndChannel != NULL);
  725.     
  726.     SSpSource_New(&drone->autoSource);
  727.     assert(drone->autoSource != NULL);
  728.     
  729.     link.description.componentType            = kSoundEffectsType;
  730.     link.description.componentSubType        = kSSpLocalizationSubType;
  731.     link.description.componentManufacturer    = 0;
  732.     link.description.componentFlags            = 0;        
  733.     link.description.componentFlagsMask        = 0;    
  734.     link.mixerID                            = nil;
  735.     link.linkID                                = nil;
  736.     
  737.     SndSetInfo(drone->autoSndChannel, siPreMixerSoundComponent, &link);
  738.     
  739.     // Verify that the right version filter was installed
  740.     if (gDroneAutoCheckFilterVersion)
  741.     {
  742.         err = GetSSpFilterVersion(
  743.                 link.description.componentManufacturer,
  744.                 &manufacturer,
  745.                 &majorVersion,
  746.                 &minorVersion);
  747.         
  748.         if (err != noErr)
  749.         {
  750.             // Filter must not be installed
  751.             // Note: A normal application would bail on 3D sound here
  752.             StopAlert(kAlrtID_FilterNotInstalled, NULL);
  753.         }
  754.         else
  755.         {
  756.             // The major version represents the component specification level
  757.             if (majorVersion < 1)
  758.             {
  759.                 //• if we couldn't handle some old version, we could bail here.
  760.                 
  761.                 StopAlert(kAlrtID_FilterVersion, NULL);
  762.                 // Note: A normal application would bail on 3D sound here
  763.                 gSoundOn = false;
  764.             }
  765.             else
  766.             {
  767.                 // The minor version specifies the manufacturer's implementation revision
  768.                 
  769.                 //• could do something here is we needed to handle tweaks for specific
  770.                 //• manufacturers or versions.
  771.             }
  772.         }
  773.         
  774.         gDroneAutoCheckFilterVersion = false;
  775.     }
  776.  
  777.     if (gSoundOn)
  778.     {
  779.         // The sound is loudest out the back of the model
  780.         Q3Vector3D_Set(&orientation, -1.0, 0.0, 0.0);
  781.         SSpSource_SetOrientation(drone->autoSource, &orientation);
  782.     }
  783.     
  784.     drone->autoInterest        = inDroneOfInterest;
  785.     drone->geometry            = Q3Shared_GetReference(gDroneAutoGeometry);
  786.     
  787.     drone->position.x        += Random()*0.0001 + AUTO_DRONE_LEAD;
  788.     drone->position.y        += Random()*0.0001;
  789.     drone->position.z        += Random()*0.0001;
  790.     
  791.     drone->autoVelocity.x    += Random()*0.0001;
  792.     drone->autoVelocity.y    += Random()*0.0001;
  793.     drone->autoVelocity.z    += Random()*0.0001;
  794.     
  795.     // Validate it
  796.     drone->thumbprint        = kThumbprint_AutoDrone;
  797.     
  798.     return drone;
  799. }
  800.  
  801.  
  802. /* =============================================================================
  803.  *        BulletDrone_New (external)
  804.  *
  805.  *    Creates a new drone whose behavior is projectile.
  806.  * ========================================================================== */
  807. TDroneObject BulletDrone_New(
  808.     const TQ3Point3D*        inPosition,
  809.     const TQ3Vector3D*        inDirection)
  810. {
  811.     TDroneObject            drone;
  812.     
  813.     assert(inPosition != NULL);
  814.     assert(inDirection != NULL);
  815.     
  816.     // Create the basic drone
  817.     drone = Drone_New(kDroneOrder_Bullet);
  818.     assert(drone != NULL);
  819.     
  820.     // Fill in the fields
  821.     drone->moveMethod        = BulletDrone_Move;
  822.     drone->submitMethod        = BulletDrone_Submit;
  823.     
  824.     drone->position            = *inPosition;
  825.     drone->direction        = *inDirection;
  826.     
  827.     drone->bulletOrigin        = *inPosition;
  828.     
  829.     // Validate it
  830.     drone->thumbprint        = kThumbprint_BulletDrone;
  831.     
  832.     return drone;
  833. }
  834.  
  835.  
  836. /* =============================================================================
  837.  *        Drone_Dispose (external)
  838.  *
  839.  *    Disposes of the drone.
  840.  * ========================================================================== */
  841. void Drone_Dispose(
  842.     TDroneObject            inDrone)
  843. {
  844.     assert(inDrone != NULL && IS_DRONE(inDrone));
  845.     
  846.     // Unlink it from the list
  847.     if (inDrone->prev != NULL)
  848.     {
  849.         inDrone->prev->next = inDrone->next;
  850.     }
  851.     else
  852.     {
  853.         gDroneList = inDrone->next;
  854.     }
  855.     
  856.     if (inDrone->next != NULL)
  857.     {
  858.         inDrone->next->prev = inDrone->prev;
  859.     }
  860.     
  861.     // Dispose stuff
  862.     if (inDrone->geometry != NULL)
  863.     {
  864.         Q3Object_Dispose(inDrone->geometry);
  865.         inDrone->geometry = NULL;
  866.     }
  867.     
  868.     // Free the sound channel
  869.     //• This rightly belongs in AutoDrone_Dispose
  870.     if (inDrone->autoSource != NULL)
  871.     {
  872.         SSpSource_Dispose(inDrone->autoSource);
  873.         inDrone->autoSource = NULL;
  874.     }
  875.     
  876.     if (inDrone->autoSndChannel != NULL)
  877.     {
  878.         SndDisposeChannel(inDrone->autoSndChannel, true);
  879.         inDrone->autoSndChannel = NULL;
  880.     }
  881.     
  882.     // Dispose of the memory
  883.     inDrone->thumbprint = kThumbprint_Dead;
  884.     free(inDrone);
  885. }
  886.  
  887.  
  888. /* =============================================================================
  889.  *        Drone_Next (external)
  890.  *
  891.  *    If inDrone is NULL, then the head of the drone list is returned.  If inDrone
  892.  *    is non-NULL, then the next drone in the list is returned.  If inDrone is the
  893.  *    last drone, then NULL is returned.
  894.  * ========================================================================== */
  895. TDroneObject Drone_Next(
  896.     TDroneObject            inDrone)
  897. {
  898.     TDroneObject            result;
  899.     
  900.     if (inDrone != NULL)
  901.     {
  902.         assert(IS_DRONE(inDrone));
  903.         
  904.         result = inDrone->next;
  905.     }
  906.     else
  907.     {
  908.         result = gDroneList;
  909.     }
  910.     
  911.     return result;
  912. }
  913.  
  914.  
  915. /* =============================================================================
  916.  *        Drone_Move (external)
  917.  *
  918.  *    Moves the drone forward one time step.  This may mark the drone for death.
  919.  * ========================================================================== */
  920. void Drone_Move(
  921.     TDroneObject            inDrone)
  922. {
  923.     assert(inDrone != NULL && IS_DRONE(inDrone));
  924.     
  925.     // Move the drone by its own rules
  926.     assert(inDrone->moveMethod != NULL);
  927.     (inDrone->moveMethod)(inDrone);
  928.     
  929.     // Drone is still alive -- compute the velocity and acceleration
  930.     Q3Point3D_Subtract(&inDrone->position, &inDrone->position1, &inDrone->velocity);
  931.     Q3Vector3D_Scale(&inDrone->velocity, gGameFramesPerSecond, &inDrone->velocity);
  932.     inDrone->position1 = inDrone->position;
  933.     
  934.     Q3Vector3D_Subtract(&inDrone->velocity, &inDrone->velocity1, &inDrone->acceleration);
  935.     Q3Vector3D_Scale(&inDrone->acceleration, gGameFramesPerSecond, &inDrone->acceleration);
  936.     inDrone->velocity1 = inDrone->velocity;
  937.     
  938.     // Reorthogonalize the up and cross vectors
  939.     assert(fabs(Q3Vector3D_Length(&inDrone->direction) - 1.0) < 0.05);
  940.     
  941.     Q3Vector3D_Cross(&inDrone->direction, &inDrone->up, &inDrone->cross);
  942.     Q3Vector3D_Normalize(&inDrone->cross, &inDrone->cross);
  943.     
  944.     Q3Vector3D_Cross(&inDrone->cross, &inDrone->direction, &inDrone->up);
  945. }
  946.  
  947.  
  948.  
  949. /* =============================================================================
  950.  *        AutoDrone_Move (internal)
  951.  *
  952.  *    Moves the drone forward one time step.  The autopilot drone has a constant
  953.  *    thrust along its direction of orientation.  The direction is controlled to
  954.  *    point just in front of the drone of interest.
  955.  * ========================================================================== */
  956. void AutoDrone_Move(
  957.     TDroneObject            inDrone)
  958. {
  959.     TQ3Vector3D                newDirection;
  960.     TQ3Vector3D                v1;
  961.     TQ3Vector3D                v2;
  962.     TQ3Point3D                target;
  963.     float                    distance;
  964.     float                    turnRate;
  965.     float                    limit;
  966.     TAutoSound                newSound;
  967.     SndCommand                sndCommand;
  968.     long                    base;
  969.     TQ3Matrix4x4            matrix;
  970.     
  971.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  972.     
  973.     // Figure out new direction
  974.     if (inDrone->autoMode == kAutoMode_Explosion)
  975.     {
  976.         // Point the explosion geometry at the camera
  977.         //• NOTE: For now, we assume that the camera is at the drone of interest
  978.         //• This is wrong for two reasons.  First, the camera could actually be
  979.         //• elsewhere.  Second, this assumes that the drone of interest moves
  980.         //• before this drone.
  981.         Q3Point3D_Subtract(
  982.                 &inDrone->autoInterest->position,
  983.                 &inDrone->position,
  984.                 &newDirection);
  985.         
  986.         Q3Vector3D_Normalize(&newDirection, &inDrone->direction);
  987.     }
  988.     else
  989.     {
  990.         // Find a point in front of the drone of interest
  991.         assert(IS_DRONE(inDrone->autoInterest));
  992.         
  993.         Q3Vector3D_Scale(&inDrone->autoInterest->direction, AUTO_DRONE_LEAD, &v1);
  994.         Q3Point3D_Vector3D_Add(&inDrone->autoInterest->position, &v1, &target);
  995.         
  996.         // Point toward the target
  997.         Q3Point3D_Subtract(&target, &inDrone->position, &newDirection);
  998.         Q3Vector3D_Normalize(&newDirection, &newDirection);
  999.         
  1000.         // Limit the turn rate
  1001.         turnRate = acosf(Q3Vector3D_Dot(&inDrone->direction, &newDirection));
  1002.         limit = AUTO_DRONE_TURN_RATE*gGameInterval;
  1003.         if (turnRate > limit)
  1004.         {
  1005.             // Limit the turn
  1006.             // Note: this should actually be spherical interpolation -- but linear is close enough
  1007.             turnRate = limit/turnRate;
  1008.             Q3Vector3D_Scale(&inDrone->direction, 1.0-turnRate, &v1);
  1009.             Q3Vector3D_Scale(&newDirection, turnRate, &v2);
  1010.             Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  1011.             Q3Vector3D_Normalize(&inDrone->direction, &inDrone->direction);
  1012.         }
  1013.         else
  1014.         {
  1015.             // It's OK to make the desired turn
  1016.             inDrone->direction = newDirection;
  1017.         }
  1018.     }
  1019.     
  1020.     // Figure out new position
  1021.     switch (inDrone->autoMode)
  1022.     {
  1023.         case kAutoMode_Idle: // Start a burn if we are getting farther from the target
  1024.             // Start burn if we are getting farther from the target
  1025.             distance = Q3Point3D_Distance(&inDrone->position, &target);
  1026.             if (inDrone->autoDistance > distance)
  1027.             {
  1028.                 // Getting closer to target -- don't change
  1029.                 inDrone->autoDistance = distance;
  1030.             }
  1031.             else
  1032.             {
  1033.                 // Getting farther from target -- start a burn
  1034.                 inDrone->autoMode = kAutoMode_Burn;
  1035.                 inDrone->autoModeTimeout = TickCount() + (unsigned long) (AUTO_DRONE_BURN_TIME*60);
  1036.             }
  1037.         break;
  1038.         
  1039.         case kAutoMode_Burn: // Accelerate toward the target
  1040.             // Continue the burn?
  1041.             if (TickCount() <= inDrone->autoModeTimeout)
  1042.             {
  1043.                 // Still burning -- change the velocity by accelerating toward the target
  1044.                 Q3Vector3D_Scale(&inDrone->direction, gGameInterval*AUTO_DRONE_ACCEL, &v1);
  1045.                 Q3Vector3D_Add(&inDrone->autoVelocity, &v1, &inDrone->autoVelocity);
  1046.             }
  1047.             else
  1048.             {
  1049.                 // Switch to rest mode
  1050.                 inDrone->autoMode = kAutoMode_Rest;
  1051.                 inDrone->autoModeTimeout = TickCount() + (unsigned long) (AUTO_DRONE_REST_TIME*60);
  1052.             }
  1053.         break;
  1054.         
  1055.         case kAutoMode_Rest: // Don't use the engine for a while
  1056.             // Continue the rest?
  1057.             if (TickCount() <= inDrone->autoModeTimeout)
  1058.             {
  1059.                 // Still resting
  1060.                 // (do nothing)
  1061.             }
  1062.             else
  1063.             {
  1064.                 // Switch to idle mode
  1065.                 inDrone->autoMode = kAutoMode_Idle;
  1066.                 inDrone->autoDistance = Q3Point3D_Distance(&inDrone->position, &target);
  1067.             }
  1068.         break;
  1069.         
  1070.         case kAutoMode_Explosion: // Show the explosion for a while
  1071.             // Continue the explosion?
  1072.             inDrone->autoExplosion += gGameInterval/AUTO_DRONE_EXPLOSION_TIME;
  1073.             if (inDrone->autoExplosion <= 1.0)
  1074.             {
  1075.                 // Still exploding
  1076.                 inDrone->autoVelocity.x =
  1077.                 inDrone->autoVelocity.y =
  1078.                 inDrone->autoVelocity.z = 0.0;
  1079.             }
  1080.             else
  1081.             {
  1082.                 // Kill the drone
  1083.                 Drone_SetMark(inDrone, true);
  1084.             }
  1085.         break;
  1086.         
  1087.         default:
  1088.             assert(0);
  1089.     }
  1090.     
  1091.     // Move along the new velocity vector
  1092.     Q3Vector3D_Scale(&inDrone->autoVelocity, gGameInterval, &v1);
  1093.     Q3Point3D_Vector3D_Add(&inDrone->position, &v1, &inDrone->position);
  1094.     
  1095.     if (gSoundOn)
  1096.     {
  1097.         // Choose the next sound to play
  1098.         switch (inDrone->autoMode)
  1099.         {
  1100.             case kAutoMode_Idle:
  1101.             case kAutoMode_Rest:
  1102.                 newSound = kAutoSound_Idle;
  1103.             break;
  1104.             
  1105.             case kAutoMode_Burn:
  1106.                 newSound = kAutoSound_Burn;
  1107.             break;
  1108.             
  1109.             break;
  1110.             
  1111.             case kAutoMode_Explosion:
  1112.                 newSound = kAutoSound_Explosion;
  1113.             break;
  1114.             
  1115.             default:
  1116.                 assert(0);
  1117.         }
  1118.         
  1119.         // Change the sound
  1120.         if (inDrone->autoSound != newSound)
  1121.         {
  1122.             // Stop the old sound
  1123.             if (inDrone->autoSound != kAutoSound_None)
  1124.             {
  1125.                 sndCommand.cmd = quietCmd;
  1126.                 sndCommand.param1 = 0;
  1127.                 sndCommand.param2 = 0;
  1128.                 SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1129.             }
  1130.             
  1131.             inDrone->autoSound = newSound;
  1132.             
  1133.             // Play the new sound
  1134.             switch (inDrone->autoSound)
  1135.             {
  1136.                 case kAutoSound_None:
  1137.                     base = 0;
  1138.                 break;
  1139.                 
  1140.                 case kAutoSound_Idle:
  1141.                     base = (long) *gDroneAutoSndIdle + gDroneAutoSndIdleOffset;
  1142.                     SSpSource_SetAngularAttenuation(inDrone->autoSource, 0.0, 0.0);
  1143.                     SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_IDLE_REF_DIST);
  1144.                 break;
  1145.                 
  1146.                 case kAutoSound_Burn:
  1147.                     base = (long) *gDroneAutoSndBurn + gDroneAutoSndBurnOffset;
  1148.                     SSpSource_SetAngularAttenuation(inDrone->autoSource, 1.5, -12.0);
  1149.                     SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_BURN_REF_DIST);
  1150.                 break;
  1151.                 
  1152.                 case kAutoSound_Explosion:
  1153.                     base = (long) *gDroneAutoSndExplosion + gDroneAutoSndExplosionOffset;
  1154.                     SSpSource_SetAngularAttenuation(inDrone->autoSource, 0.0, 0.0);
  1155.                     SSpSource_SetReferenceDistance(inDrone->autoSource, AUTO_DRONE_EXPL_REF_DIST);
  1156.                 break;
  1157.                 
  1158.                 default:
  1159.                     assert(0);
  1160.             }
  1161.             
  1162.             if (base != 0)
  1163.             {
  1164.                 // Install the sound
  1165.                 sndCommand.cmd = soundCmd;
  1166.                 sndCommand.param1 = 0;
  1167.                 sndCommand.param2 = base;
  1168.                 SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1169.                 
  1170.                 // Play it indefinitely
  1171.                 sndCommand.cmd = freqCmd;
  1172.                 sndCommand.param1 = 0;
  1173.                 sndCommand.param2 = 60;
  1174.                 SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1175.             }
  1176.         }
  1177.         
  1178.         // Change the sound source location
  1179.         Drone_GetMatrix(inDrone, &matrix);
  1180.         SSpSource_SetTransform(inDrone->autoSource, &matrix);
  1181.     }
  1182. }
  1183.  
  1184.  
  1185. /* =============================================================================
  1186.  *        BulletDrone_Move (internal)
  1187.  *
  1188.  *    Moves the drone forward one time step.
  1189.  * ========================================================================== */
  1190. void BulletDrone_Move(
  1191.     TDroneObject            inDrone)
  1192. {
  1193.     TQ3Vector3D                v;
  1194.     float                    prevDistance;
  1195.     float                    currDistance;
  1196.     TQ3CameraPlacement        placement;
  1197.     TDroneObject            target;
  1198.     unsigned long            count;
  1199.     unsigned long            index;
  1200.     
  1201.     assert(inDrone != NULL && IS_BULLET_DRONE(inDrone));
  1202.     
  1203.     // Move the bullet
  1204.     prevDistance = Q3Point3D_Distance(&inDrone->position, &inDrone->bulletOrigin);
  1205.     
  1206.     Q3Vector3D_Scale(&inDrone->direction, gGameInterval*BULLET_DRONE_SPEED, &v);
  1207.     Q3Point3D_Vector3D_Add(&inDrone->position, &v, &inDrone->position);
  1208.     
  1209.     currDistance = Q3Point3D_Distance(&inDrone->position, &inDrone->bulletOrigin);
  1210.     
  1211.     // Time to expire?
  1212.     if (currDistance > BULLET_DRONE_LIMIT)
  1213.     {
  1214.         // Mark the drone to die
  1215.         Drone_SetMark(inDrone, true);
  1216.     }
  1217.     else
  1218.     {
  1219.         // Set up for collision detection
  1220.         placement.cameraLocation    = inDrone->bulletOrigin;
  1221.         placement.upVector            = inDrone->up;
  1222.         
  1223.         Q3Point3D_Vector3D_Add(&inDrone->bulletOrigin, &inDrone->direction, &placement.pointOfInterest);
  1224.         
  1225.         Q3Camera_SetPlacement(gDroneCamera, &placement);
  1226.         
  1227.         // Collision detection with all target drones
  1228.         Q3View_StartPicking(gDroneView, gDronePick);
  1229.         do
  1230.         {
  1231.             for (target = Drone_Next(NULL); target != NULL; target = Drone_Next(target))
  1232.             {
  1233.                 // Submit the drone geometry, along with a PickID that is the object reference
  1234.                 Q3PickIDStyle_Submit((unsigned long) target, gDroneView);
  1235.                 Drone_PickSubmit(target, gDroneView);
  1236.             }
  1237.         }
  1238.         while (Q3View_EndPicking(gDroneView) == kQ3ViewStatusRetraverse);
  1239.         
  1240.         // Check the hit list
  1241.         Q3Pick_GetNumHits(gDronePick, &count);
  1242.         for (index = 0; index < count; index++)
  1243.         {
  1244.         UInt32    pickGood, maskGood;
  1245.         
  1246.             Q3Pick_GetPickDetailData(gDronePick, index, kQ3PickDetailMaskPickID, &pickGood);
  1247.             Q3Pick_GetPickDetailData(gDronePick, index, kQ3PickDetailMaskDistance, &maskGood);
  1248.             
  1249.             if (pickGood && maskGood)
  1250.             {
  1251.                 Q3Pick_GetPickDetailData(gDronePick, index, kQ3PickDetailMaskPickID, &target);
  1252.                 
  1253.                 if (target != NULL && IS_DRONE(target))
  1254.                 {
  1255.                     // Got a valid hit -- check its range
  1256.                     //• Should it be a bullet or a laser?
  1257. //                    if ((1 || prevDistance <= hitData.distance) && hitData.distance <= currDistance)
  1258.                     if (true)
  1259.                     {
  1260.                         // Hit it!
  1261.                         Drone_Hit(target);
  1262.                         
  1263.                         // Kill the bullet
  1264.                         Drone_SetMark(inDrone, true);
  1265.                         break;
  1266.                     }
  1267.                 }
  1268.             }
  1269.             
  1270. //            Q3Hit_EmptyData(&hitData);
  1271.         }
  1272.         
  1273.         // Empty out the pick hits
  1274.         Q3Pick_EmptyHitList(gDronePick);
  1275.     }
  1276. }
  1277.  
  1278.  
  1279. /* =============================================================================
  1280.  *        Drone_UpdateSound (external)
  1281.  *
  1282.  *    Updates localized sounds for this drone.  The default method does nothing.
  1283.  * ========================================================================== */
  1284. void Drone_UpdateSound(
  1285.     TDroneObject            inDrone)
  1286. {
  1287.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1288.     
  1289.     if (inDrone->updateSoundMethod != NULL)
  1290.     {
  1291.         (inDrone->updateSoundMethod)(inDrone);
  1292.     }
  1293. }
  1294.  
  1295.  
  1296. /* =============================================================================
  1297.  *        AutoDrone_UpdateSound (external)
  1298.  *
  1299.  *    Updates localized sounds for this autopilot drone.
  1300.  * ========================================================================== */
  1301. void AutoDrone_UpdateSound(
  1302.     TDroneObject            inDrone)
  1303. {
  1304.     SSpLocalizationData                snd3DInfo;
  1305.     
  1306.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1307.     
  1308.     if (gSoundOn)
  1309.     {
  1310.         SSpSource_CalcLocalization(inDrone->autoSource, Sound_GetListener(), &snd3DInfo);
  1311.         
  1312.         SndSetInfo(inDrone->autoSndChannel, siSSpLocalization, &snd3DInfo);
  1313.     }
  1314. }
  1315.  
  1316.  
  1317. /* =============================================================================
  1318.  *        Drone_Submit (external)
  1319.  *
  1320.  *    Submits the drone for drawing.
  1321.  * ========================================================================== */
  1322. void Drone_Submit(
  1323.     TDroneObject            inDrone,
  1324.     Boolean                    inHUDVisible,
  1325.     TQ3ViewObject            inView)
  1326. {
  1327.     TQ3Matrix4x4            matrix;
  1328.     
  1329.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1330.     
  1331.     if (inDrone->submitMethod != NULL)
  1332.     {
  1333.         // Use the submit method
  1334.         (inDrone->submitMethod)(inDrone, inHUDVisible, inView);
  1335.     }
  1336.     else if (inDrone->geometry != NULL)
  1337.     {
  1338.         // Submit the geometry
  1339.         Drone_GetMatrix(inDrone, &matrix);
  1340.         
  1341.         Q3Push_Submit(inView);
  1342.         Q3MatrixTransform_Submit(&matrix, inView);
  1343.         Q3Object_Submit(inDrone->geometry, inView);
  1344.         Q3Pop_Submit(inView);
  1345.     }
  1346. }
  1347.  
  1348.  
  1349. /* =============================================================================
  1350.  *        AutoDrone_Submit (internal)
  1351.  *
  1352.  *    Submits the bullet drone for drawing.
  1353.  * ========================================================================== */
  1354. void AutoDrone_Submit(
  1355.     TDroneObject            inDrone,
  1356.     Boolean                    inHUDVisible,
  1357.     TQ3ViewObject            inView)
  1358. {
  1359.     TQ3Matrix4x4            matrix;
  1360.     TQ3ColorRGB                transparency;
  1361.     TQ3Point3D                position;
  1362.     TQ3Vector3D                direction;
  1363.     TQ3Vector3D                up;
  1364.     TQ3Vector3D                right;
  1365.     TQ3Vector3D                v;
  1366.     TQ3Vector3D                v1;
  1367.     TQ3Vector3D                v2;
  1368.     float                    height;
  1369.     TQ3GeometryObject        marker;
  1370.     
  1371.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1372.     assert(inView != NULL);
  1373.     
  1374.     // Draw the drone
  1375.     Q3Push_Submit(inView);
  1376.     
  1377.     Drone_GetMatrix(inDrone, &matrix);
  1378.     Q3MatrixTransform_Submit(&matrix, inView);
  1379.     
  1380.     switch (inDrone->autoMode)
  1381.     {
  1382.         case kAutoMode_Burn:
  1383.             Q3Object_Submit(gDroneAutoBurnGeometry, inView);
  1384.             /* FALL THROUGH TO SUBMIT DRONE GEOMETRY */
  1385.         
  1386.         case kAutoMode_Rest:
  1387.         case kAutoMode_Idle:
  1388.             Q3Object_Submit(inDrone->geometry, inView);
  1389.         break;
  1390.         
  1391.         case kAutoMode_Explosion:
  1392.             // Grow the explosion
  1393.             v.x = v.y = v.z = (1.0-AUTO_DRONE_MIN_SCALE)*inDrone->autoExplosion + AUTO_DRONE_MIN_SCALE;
  1394.             Q3ScaleTransform_Submit(&v, inView);
  1395.             
  1396.             // Fade at the end
  1397.             if (inDrone->autoExplosion > (1.0-AUTO_DRONE_FADE_TIME))
  1398.             {
  1399.                 transparency.r =
  1400.                 transparency.g =
  1401.                 transparency.b = (-1.0/AUTO_DRONE_FADE_TIME)*inDrone->autoExplosion + (1.0/AUTO_DRONE_FADE_TIME);
  1402.                 
  1403.                 Q3Attribute_Submit(kQ3AttributeTypeTransparencyColor, &transparency, inView);
  1404.             }
  1405.             
  1406.             // Submit the explosion geometry
  1407.             Q3Object_Submit(gDroneAutoExplosionGeometry, inView);
  1408.         break;
  1409.         
  1410.         default:
  1411.             assert(0);
  1412.     }
  1413.     
  1414.     Q3Pop_Submit(inView);
  1415.     
  1416.     // Draw the HUD marker for the drone
  1417.     if (inHUDVisible && inDrone->autoMode != kAutoMode_Explosion)
  1418.     {
  1419.         Display_GetViewerPosition(&position, &direction, &up);
  1420.         Q3Vector3D_Cross(&direction, &up, &right);
  1421.         
  1422.         Q3Point3D_Subtract(&inDrone->position, &position, &v);
  1423.         
  1424.         Q3Vector3D_Scale(&up,    HUD_SCALE*Q3Vector3D_Dot(&direction, &v), &v1);
  1425.         Q3Vector3D_Scale(&right, HUD_SCALE*Q3Vector3D_Dot(&right,     &v), &v2);
  1426.         
  1427.         Q3Point3D_Vector3D_Add(&position, &direction, &position);
  1428.         Q3Point3D_Vector3D_Add(&position, &v1,        &position);
  1429.         Q3Point3D_Vector3D_Add(&position, &v2,        &position);
  1430.         
  1431.         Q3Vector3D_Normalize(&v, &v);
  1432.         height = Q3Vector3D_Dot(&up, &v);
  1433.         if (height >= HUD_HEIGHT)
  1434.         {
  1435.             marker = gDroneAutoMarkerAbove;
  1436.         }
  1437.         else if (height <= -HUD_HEIGHT)
  1438.         {
  1439.             marker = gDroneAutoMarkerBelow;
  1440.         }
  1441.         else
  1442.         {
  1443.             marker = gDroneAutoMarkerEqual;
  1444.         }
  1445.         
  1446.         Q3Marker_SetPosition(marker, &position);
  1447.         Q3Object_Submit(marker, inView);
  1448.     }
  1449. }
  1450.  
  1451.  
  1452. /* =============================================================================
  1453.  *        BulletDrone_Submit (internal)
  1454.  *
  1455.  *    Submits the bullet drone for drawing.
  1456.  * ========================================================================== */
  1457. void BulletDrone_Submit(
  1458.     TDroneObject            inDrone,
  1459.     Boolean                    inHUDVisible,
  1460.     TQ3ViewObject            inView)
  1461. {
  1462.     TQ3LineData                lineData;
  1463.     
  1464.     assert(inDrone != NULL && IS_BULLET_DRONE(inDrone));
  1465.     assert(inView != NULL);
  1466.     
  1467.     lineData.vertices[0].point            = inDrone->bulletOrigin;
  1468.     lineData.vertices[0].attributeSet    = NULL;
  1469.     lineData.vertices[1].point            = inDrone->position;
  1470.     lineData.vertices[1].attributeSet    = NULL;
  1471.     lineData.lineAttributeSet            = NULL;
  1472.     
  1473.     Q3Push_Submit(inView);
  1474.     Q3Object_Submit(gDroneNULLIllumination, inView);
  1475.     Q3Object_Submit(gDroneBulletColor, inView);
  1476.     Q3Line_Submit(&lineData, inView);
  1477.     Q3Pop_Submit(inView);
  1478. }
  1479.  
  1480.  
  1481. /* =============================================================================
  1482.  *        Drone_PickSubmit (internal)
  1483.  *
  1484.  *    Submits the drone for picking against a bullet.  It forwards to the actual
  1485.  *    drone hit method, if any.
  1486.  * ========================================================================== */
  1487. void Drone_PickSubmit(
  1488.     TDroneObject            inDrone,
  1489.     TQ3ViewObject            inView)
  1490. {
  1491.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1492.     
  1493.     if (inDrone->pickSubmitMethod != NULL)
  1494.     {
  1495.         (*inDrone->pickSubmitMethod)(inDrone, inView);
  1496.     }
  1497. }
  1498.  
  1499.  
  1500. /* =============================================================================
  1501.  *        AutoDrone_PickSubmit (internal)
  1502.  *
  1503.  *    Submits the autopilot drone for picking against a bullet.
  1504.  * ========================================================================== */
  1505. void AutoDrone_PickSubmit(
  1506.     TDroneObject            inDrone,
  1507.     TQ3ViewObject            inView)
  1508. {
  1509.     TQ3Matrix4x4            matrix;
  1510.     
  1511.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1512.     assert(inView != NULL);
  1513.     
  1514.     // Draw the drone
  1515.     if (inDrone->autoMode != kAutoMode_Explosion)
  1516.     {
  1517.         Q3Push_Submit(inView);
  1518.         
  1519.         Drone_GetMatrix(inDrone, &matrix);
  1520.         Q3MatrixTransform_Submit(&matrix, inView);
  1521.         
  1522.         Q3Object_Submit(gDroneAutoBurnGeometry, inView);
  1523.         
  1524.         Q3Pop_Submit(inView);
  1525.     }
  1526. }
  1527.  
  1528.  
  1529. /* =============================================================================
  1530.  *        Drone_Hit (internal)
  1531.  *
  1532.  *    Called when this drone is hit.  It forwards to the actual drone hit method,
  1533.  *    if any.
  1534.  * ========================================================================== */
  1535. void Drone_Hit(
  1536.     TDroneObject            inDrone)
  1537. {
  1538.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1539.     
  1540.     if (inDrone->hitMethod != NULL)
  1541.     {
  1542.         (*inDrone->hitMethod)(inDrone);
  1543.     }
  1544. }
  1545.  
  1546.  
  1547. /* =============================================================================
  1548.  *        AutoDrone_Hit (internal)
  1549.  *
  1550.  *    Called when this autopilot drone is hit.  It puts the drone into
  1551.  *    explosion mode.
  1552.  * ========================================================================== */
  1553. void AutoDrone_Hit(
  1554.     TDroneObject            inDrone)
  1555. {
  1556.     assert(inDrone != NULL && IS_AUTO_DRONE(inDrone));
  1557.     
  1558.     inDrone->autoMode = kAutoMode_Explosion;
  1559.     inDrone->autoExplosion = 0.0;
  1560. }
  1561.  
  1562.  
  1563. /* =============================================================================
  1564.  *        Drone_SetMark (external)
  1565.  *
  1566.  *    Changes the drone's mark to the given value.  The mark is used to indicate
  1567.  *    which drones should die.
  1568.  * ========================================================================== */
  1569. void Drone_SetMark(
  1570.     TDroneObject            inDrone,
  1571.     Boolean                    inMark)
  1572. {
  1573.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1574.     
  1575.     inDrone->mark = inMark;
  1576. }
  1577.  
  1578.  
  1579. /* =============================================================================
  1580.  *        Drone_GetMark (external)
  1581.  *
  1582.  *    Returns the drone's mark.
  1583.  * ========================================================================== */
  1584. Boolean Drone_GetMark(
  1585.     TDroneObject            inDrone)
  1586. {
  1587.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1588.     
  1589.     return inDrone->mark;
  1590. }
  1591.  
  1592.  
  1593. /* =============================================================================
  1594.  *        Drone_GetPosition (external)
  1595.  *
  1596.  *    Returns the current position in outPosition.
  1597.  * ========================================================================== */
  1598. void Drone_GetPosition(
  1599.     TDroneObject            inDrone,
  1600.     TQ3Point3D*                outPosition)
  1601. {
  1602.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1603.     assert(outPosition != NULL);
  1604.     
  1605.     *outPosition = inDrone->position;
  1606. }
  1607.  
  1608.  
  1609. /* =============================================================================
  1610.  *        Drone_GetVelocity (external)
  1611.  *
  1612.  *    Returns the current velocity in outVelocity
  1613.  * ========================================================================== */
  1614. void Drone_GetVelocity(
  1615.     TDroneObject            inDrone,
  1616.     TQ3Vector3D*            outVelocity)
  1617. {
  1618.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1619.     assert(outVelocity != NULL);
  1620.     
  1621.     *outVelocity = inDrone->velocity;
  1622. }
  1623.  
  1624.  
  1625. /* =============================================================================
  1626.  *        Drone_GetDirection (external)
  1627.  *
  1628.  *    Returns the current direction in outDirection.
  1629.  * ========================================================================== */
  1630. void Drone_GetDirection(
  1631.     TDroneObject            inDrone,
  1632.     TQ3Vector3D*            outDirection)
  1633. {
  1634.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1635.     assert(outDirection != NULL);
  1636.     
  1637.     *outDirection = inDrone->direction;
  1638. }
  1639.  
  1640.  
  1641. /* =============================================================================
  1642.  *        Drone_GetUp (external)
  1643.  *
  1644.  *    Returns the current up vector in outUp.
  1645.  * ========================================================================== */
  1646. void Drone_GetUp(
  1647.     TDroneObject            inDrone,
  1648.     TQ3Vector3D*            outUp)
  1649. {
  1650.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1651.     assert(outUp != NULL);
  1652.     
  1653.     *outUp = inDrone->up;
  1654. }
  1655.  
  1656.  
  1657. /* =============================================================================
  1658.  *        Drone_GetMatrix (external)
  1659.  *
  1660.  *    Returns the matrix that transforms to the drone position and orientation.
  1661.  * ========================================================================== */
  1662. void Drone_GetMatrix(
  1663.     TDroneObject            inDrone,
  1664.     TQ3Matrix4x4*            outMatrix)
  1665. {
  1666.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1667.     assert(outMatrix != NULL);
  1668.     
  1669.     outMatrix->value[0][0] = inDrone->direction.x;
  1670.     outMatrix->value[0][1] = inDrone->direction.y;
  1671.     outMatrix->value[0][2] = inDrone->direction.z;
  1672.     
  1673.     outMatrix->value[1][0] = inDrone->up.x;
  1674.     outMatrix->value[1][1] = inDrone->up.y;
  1675.     outMatrix->value[1][2] = inDrone->up.z;
  1676.     
  1677.     outMatrix->value[2][0] = inDrone->cross.x;
  1678.     outMatrix->value[2][1] = inDrone->cross.y;
  1679.     outMatrix->value[2][2] = inDrone->cross.z;
  1680.     
  1681.     outMatrix->value[3][0] = inDrone->position.x;
  1682.     outMatrix->value[3][1] = inDrone->position.y;
  1683.     outMatrix->value[3][2] = inDrone->position.z;
  1684.     
  1685.     outMatrix->value[0][3] = 0.0;
  1686.     outMatrix->value[1][3] = 0.0;
  1687.     outMatrix->value[2][3] = 0.0;
  1688.     outMatrix->value[3][3] = 1.0;
  1689. }
  1690.  
  1691.  
  1692. /* =============================================================================
  1693.  *        Drone_Fire (external)
  1694.  *
  1695.  *    Called each time the fire button is pressed.
  1696.  * ========================================================================== */
  1697. void Drone_Fire(
  1698.     TDroneObject            inDrone)
  1699. {
  1700.     TQ3Vector3D                up;
  1701.     TQ3Vector3D                cross;
  1702.     TQ3Point3D                origin;
  1703.     
  1704.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1705.     
  1706.     // Find the offsets
  1707.     Q3Vector3D_Scale(&inDrone->up,        BULLET_DRONE_OFFSET, &up);
  1708.     Q3Vector3D_Scale(&inDrone->cross,    BULLET_DRONE_OFFSET, &cross);
  1709.     
  1710.     // Fire one
  1711.     Q3Point3D_Vector3D_Subtract(&inDrone->position, &up, &origin);
  1712.     Q3Point3D_Vector3D_Subtract(&origin, &cross, &origin);
  1713.     BulletDrone_New(&origin, &inDrone->direction);
  1714.     
  1715.     // Fire two
  1716.     Q3Point3D_Vector3D_Subtract(&inDrone->position, &up, &origin);
  1717.     Q3Point3D_Vector3D_Add(&origin, &cross, &origin);
  1718.     BulletDrone_New(&origin, &inDrone->direction);
  1719. }
  1720.  
  1721.  
  1722. /* =============================================================================
  1723.  *        Drone_Silence (external)
  1724.  *
  1725.  *    Silences any drone sounds.
  1726.  * ========================================================================== */
  1727. void Drone_Silence(
  1728.     TDroneObject            inDrone)
  1729. {
  1730.     SndCommand                sndCommand;
  1731.     
  1732.     assert(inDrone != NULL && IS_DRONE(inDrone));
  1733.     
  1734.     //• We should really do this as AutoDrone_Silence, but whatever...
  1735.     
  1736.     if (inDrone->autoSndChannel != NULL)
  1737.     {
  1738.         // Purge any pending commands
  1739.         sndCommand.cmd = flushCmd;
  1740.         sndCommand.param1 = 0;
  1741.         sndCommand.param2 = 0;
  1742.         SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1743.             
  1744.         // Quiet the current sound
  1745.         sndCommand.cmd = quietCmd;
  1746.         sndCommand.param1 = 0;
  1747.         sndCommand.param2 = 0;
  1748.         SndDoImmediate(inDrone->autoSndChannel, &sndCommand);
  1749.     }
  1750. }
  1751.  
  1752. /* =============================================================================
  1753.  *        SelfDrone_Move (internal)
  1754.  *
  1755.  *    Moves the drone forward one time step.
  1756.  * ========================================================================== */
  1757. void SelfDrone_Move(
  1758.     TDroneObject            inDrone)
  1759. {
  1760.     TQ3Vector3D                v;
  1761.     
  1762.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1763.     
  1764.     // Move along the velocity vector
  1765.     Q3Vector3D_Scale(&inDrone->autoVelocity, gGameInterval, &v);
  1766.     Q3Point3D_Vector3D_Add(&inDrone->position, &v, &inDrone->position);
  1767.  
  1768. #if    SELF_DRONE_HAS_FRICTION
  1769.     // automatically slow ship down if no thrust (like friction)
  1770.     Q3Vector3D_Scale(&inDrone->autoVelocity, SELF_DRONE_FRICTION, &inDrone->autoVelocity);
  1771. #endif
  1772. }
  1773.  
  1774. /* =============================================================================
  1775.  *        SelfDrone_Thrust (external)
  1776.  *
  1777.  *    Increases the velocity of the drone.
  1778.  * ========================================================================== */
  1779. void SelfDrone_Thrust(
  1780.     TDroneObject            inDrone,
  1781.     float                    inThrust)
  1782. {
  1783.     TQ3Vector3D                v;
  1784.     
  1785.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1786.     
  1787.     // change the velocity by accelerating inThrust amount
  1788.     Q3Vector3D_Scale(&inDrone->direction, inThrust*SELF_DRONE_MAX_ACCEL, &v);
  1789.     Q3Vector3D_Add(&inDrone->autoVelocity, &v, &inDrone->autoVelocity);
  1790. }
  1791.  
  1792. /* =============================================================================
  1793.  *        SelfDrone_DampVelocity (external)
  1794.  *
  1795.  *    Increases the velocity of the drone.
  1796.  * ========================================================================== */
  1797. void SelfDrone_DampVelocity(
  1798.     TDroneObject            inDrone,
  1799.     float                    inDampingPercentage)
  1800. {
  1801.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1802.     
  1803.     // automatically slow ship down if no thrust (like friction)
  1804.     Q3Vector3D_Scale(&inDrone->autoVelocity, inDampingPercentage, &inDrone->autoVelocity);
  1805. }
  1806.  
  1807. /* =============================================================================
  1808.  *        SelfDrone_AllStop (external)
  1809.  *
  1810.  *    Increases the velocity of the drone.
  1811.  * ========================================================================== */
  1812. void SelfDrone_InstantStop(
  1813.     TDroneObject            inDrone)
  1814. {
  1815.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1816.     
  1817.     // automatically slow ship down if no thrust (like friction)
  1818.     Q3Vector3D_Scale(&inDrone->autoVelocity, 0.0, &inDrone->autoVelocity);
  1819. }
  1820.  
  1821.  
  1822.  
  1823. /* =============================================================================
  1824.  *        SelfDrone_Pitch (external)
  1825.  *
  1826.  *    Changes the direction of the ship by the given angles, in radians.
  1827.  * ========================================================================== */
  1828. void SelfDrone_Pitch(
  1829.     TDroneObject            inDrone,
  1830.     float                    inPitchAngle)
  1831. {
  1832.     TQ3Vector3D                v1;
  1833.     TQ3Vector3D                v2;
  1834.     
  1835.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1836.     
  1837.     if (inPitchAngle != 0.0)
  1838.     {
  1839.         Q3Vector3D_Scale(&inDrone->direction, cosf(inPitchAngle), &v1);
  1840.         Q3Vector3D_Scale(&inDrone->up,        sinf(inPitchAngle), &v2);
  1841.         Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  1842.     }
  1843. }
  1844.  
  1845.  
  1846. /* =============================================================================
  1847.  *        SelfDrone_Yaw (external)
  1848.  *
  1849.  *    Changes the direction of the ship by the given angles, in radians.
  1850.  * ========================================================================== */
  1851. void SelfDrone_Yaw(
  1852.     TDroneObject            inDrone,
  1853.     float                    inYawAngle)
  1854. {
  1855.     TQ3Vector3D                v1;
  1856.     TQ3Vector3D                v2;
  1857.     
  1858.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1859.     
  1860.     if (inYawAngle != 0.0)
  1861.     {
  1862.         Q3Vector3D_Scale(&inDrone->direction, cosf(inYawAngle), &v1);
  1863.         Q3Vector3D_Scale(&inDrone->cross,     sinf(inYawAngle), &v2);
  1864.         Q3Vector3D_Add(&v1, &v2, &inDrone->direction);
  1865.     }
  1866. }
  1867.  
  1868.  
  1869. /* =============================================================================
  1870.  *        SelfDrone_Roll (external)
  1871.  *
  1872.  *    Changes the direction of the ship by the given angles, in radians.
  1873.  * ========================================================================== */
  1874. void SelfDrone_Roll(
  1875.     TDroneObject            inDrone,
  1876.     float                    inRollAngle)
  1877. {
  1878.     TQ3Vector3D                v1;
  1879.     TQ3Vector3D                v2;
  1880.     
  1881.     assert(inDrone != NULL && IS_SELF_DRONE(inDrone));
  1882.     
  1883.     if (inRollAngle != 0.0)
  1884.     {
  1885.         Q3Vector3D_Scale(&inDrone->up,         cosf(inRollAngle), &v1);
  1886.         Q3Vector3D_Scale(&inDrone->cross,   sinf(inRollAngle), &v2);
  1887.         Q3Vector3D_Add(&v1, &v2, &inDrone->up);
  1888.     }
  1889. }
  1890.  
  1891. /*
  1892. {
  1893.     matrix = BuildRotationMatrix (angle, x, y, z)
  1894.     
  1895.     up = Multiply (up, matrix);
  1896.     
  1897.     normalize?
  1898. }
  1899. */